/*
 * Copyright 2010 Christoph Widulle (christoph.widulle@googlemail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */


package com.android1.amarena2d.engine;

import com.android1.amarena2d.commons.collections.FixedSizeArray;
import com.android1.amarena2d.input.InputAdapter;
import com.android1.amarena2d.input.InputListener;
import com.android1.amarena2d.input.TouchSpot;

import java.util.Comparator;

public class TouchSpotSystem extends EngineObject {

    private static AmarenaCamera CACHED_CAM;
    private static float LAST_X = -9999;
    private static float LAST_Y = -9999;
    private static float[] COORDS = new float[2];

    private static int AUTO_PRIORITY = Integer.MAX_VALUE;

    public static int nextPriority() {
        return --AUTO_PRIORITY;
    }

    private final FixedSizeArray<TouchSpot> touchspots;
    private final InputListener inputListener;
    private TouchSpot exclusive;

    public TouchSpotSystem() {

        touchspots = new FixedSizeArray<TouchSpot>(engine.getConfig().get(Config.TOUCHSPOTS_MAX), new TouchSpotComparator());
        inputListener = new Listener(touchspots);
        engine.getInputProcessor().addListener(inputListener);
    }

    public static float[] translate(AmarenaCamera camera, float x, float y) {
        if (CACHED_CAM == null || CACHED_CAM != camera) {
            CACHED_CAM = camera;
            COORDS[0] = camera.getScreenToWorldX(LAST_X = x);
            COORDS[1] = camera.getScreenToWorldY(LAST_Y = y);
            return COORDS;
        } else {
            if (LAST_X != x || LAST_Y != y) {
                COORDS[0] = camera.getScreenToWorldX(LAST_X = x);
                COORDS[1] = camera.getScreenToWorldY(LAST_Y = y);
                return COORDS;
            } else {
                return COORDS;
            }
        }
    }

    public void register(TouchSpot touchSpot) {
        touchspots.add(touchSpot);
        touchspots.sort(false);
    }

    public void unregister(TouchSpot touchSpot) {
        touchspots.remove(touchSpot, true);
    }

    public TouchSpot getExclusive() {
        return exclusive;
    }

    public void setExclusive(TouchSpot exclusive) {
        this.exclusive = exclusive;
    }


    public static boolean isHit(final float x, final float y, final float px, final float py, final float width, final float height) {
        final float topleftX = px;
        final float topleftY = py + height;

        if (topleftX > x || topleftY < y)
            return false;

        final float bottomRightX = px + width;
        final float bottomRightY = py;

        return !(bottomRightX < x || bottomRightY > y);


    }

    private static final class TouchSpotComparator implements Comparator<TouchSpot> {

        @Override
        public int compare(TouchSpot o1, TouchSpot o2) {
            final int thisVal = o1.getPriority();
            final int anotherVal = o2.getPriority();
            return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
        }


    }

    private static final class Listener extends InputAdapter {

        FixedSizeArray<TouchSpot> touchspots;

        private Listener(FixedSizeArray<TouchSpot> touchspots) {
            this.touchspots = touchspots;
        }

        @Override
        public boolean keyDown(int keycode) {
            return false;
        }

        @Override
        public boolean keyUp(int keycode) {
            return false;
        }

        @Override
        public boolean keyTyped(char character) {
            return false;
        }

        @Override
        public boolean touchDown(int x, int y, int pointer) {
            boolean hitfound = false;
            final int c = touchspots.getCount();
            if (c > 0) {
                for (int i = 0; i < c; i++) {
                    TouchSpot touchSpot = touchspots.get(i);
                    if (touchSpot.isActive()) {
                        final float[] transCoords = translate(touchSpot.getCamera(), x, y);
                        final float transX = transCoords[0];
                        final float transY = transCoords[1];
                        boolean hit = false;
                        if (!hitfound) {
                            hit = touchSpot.isHit(transX, transY);
                        }
                        if (hit || touchSpot.isNotifyNoHits()) {
                            if (touchSpot.isTranslated() ? touchSpot.onTouchDown(transX, transY, pointer, hit) : touchSpot.onTouchDown(x, -y, pointer, hit))
                                hitfound = true;
                        }
                    }
                }
            }
            return hitfound;
        }


        @Override
        public boolean touchUp(int x, int y, int pointer) {
            boolean hitfound = false;
            final int c = touchspots.getCount();
            if (c > 0) {
                for (int i = 0; i < c; i++) {
                    TouchSpot touchSpot = touchspots.get(i);
                    if (touchSpot.isActive()) {
                        final float[] transCoords = translate(touchSpot.getCamera(), x, y);
                        final float transX = transCoords[0];
                        final float transY = transCoords[1];
                        boolean hit = false;
                        if (!hitfound) {
                            hit = touchSpot.isHit(transX, transY);
                        }
                        if (hit || touchSpot.isNotifyNoHits()) {
                            if (touchSpot.isTranslated() ? touchSpot.onTouchUp(transX, transY, pointer, hit) : touchSpot.onTouchUp(x, -y, pointer, hit))
                                hitfound = true;
                        }
                    }

                }
            }
            return hitfound;
        }

        @Override
        public boolean touchDragged(int x, int y, int pointer) {
            boolean hitfound = false;
            final int c = touchspots.getCount();
            if (c > 0) {
                for (int i = 0; i < c; i++) {
                    TouchSpot touchSpot = touchspots.get(i);
                    if (touchSpot.isActive()) {
                        final float[] transCoords = translate(touchSpot.getCamera(), x, y);
                        final float transX = transCoords[0];
                        final float transY = transCoords[1];
                        boolean hit = false;
                        if (!hitfound) {
                            hit = touchSpot.isHit(transX, transY);
                        }
                        if (hit || touchSpot.isNotifyNoHits()) {
                            if (touchSpot.isTranslated() ? touchSpot.onTouchDragged(transX, transY, pointer, hit) : touchSpot.onTouchDragged(x, -y, pointer, hit))
                                hitfound = true;
                        }
                    }
                }
            }
            return hitfound;
        }
    }

}
